﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Input;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Collections;
using System.ComponentModel;
namespace Centny
{
    public sealed partial class AutoCanvas : UserControl
    {
        public delegate void ElementTapped(Object item, Object sender, TappedRoutedEventArgs e);
        public delegate void SelectItems(AutoCanvas cas, NotifyCollectionChangedEventArgs arg);
        public enum LayoutType
        {
            Horizontal = 1,
            Vertical = 2,
            Random = 3,
            WaterfallHorizontal = 4,
            WaterfallVertical = 5
        }
        public class DataTemplateBuilder : DependencyObject
        {
            public delegate List<String> RandomGroupHandler(DataTemplateBuilder builder, Random rd, Double ext);
            public delegate FrameworkElement RandomHandler(DataTemplateBuilder builder, IList<Object> all, Object current, Random rd, Object ext);
            public delegate void CreateHandler(DataTemplateBuilder builder, FrameworkElement ui, IList<Object> all, Object current, Object ext);
            private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                DataTemplateBuilder builder = d as DataTemplateBuilder;
                if (e.Property.Equals(DataTemplateBuilder.SourceProperty))
                {
                    builder.TemplSet.Source = builder.Source;
                    builder.LoadTemplates(builder.TemplSet);
                }
            }
            //
            ResourceDictionary _TemplSet = new ResourceDictionary();
            public ResourceDictionary TemplSet
            {
                get
                {
                    return this._TemplSet;
                }
            }
            public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(Uri), typeof(DataTemplateBuilder), new PropertyMetadata(null, OnSourceChanged));
            public Uri Source
            {
                get
                {
                    return this.GetValue(DataTemplateBuilder.SourceProperty) as Uri;
                }
                set
                {
                    this.SetValue(DataTemplateBuilder.SourceProperty, value);
                }
            }
            List<DataTemplate> _Templates = new List<DataTemplate>();
            public List<DataTemplate> Templates
            {
                get
                {
                    return this._Templates;
                }
            }
            protected virtual void LoadTemplates(ResourceDictionary tset)
            {
                this._Templates.Clear();
                foreach (KeyValuePair<object, object> kv in tset)
                {
                    if (kv.Value.GetType().Equals(typeof(DataTemplate)))
                    {
                        this._Templates.Add(kv.Value as DataTemplate);
                    }
                }
            }
            Random rd = new Random();
            public RandomHandler OnRandomElement { get; set; }
            public CreateHandler OnCreateElement { get; set; }
            public RandomGroupHandler OnRandomGroup { get; set; }
            public virtual FrameworkElement RandomElement(IList<Object> all, Object current)
            {
                FrameworkElement ui = null;
                if (this.OnRandomElement == null)
                {
                    int tidx = rd.Next(0, this.Templates.Count);
                    ui = this.Templates[tidx].LoadContent() as FrameworkElement;
                }
                else
                {
                    ui = this.OnRandomElement(this, all, current, rd, null);
                }
                if (this.OnCreateElement != null)
                {
                    this.OnCreateElement(this, ui, all, current, null);
                }
                return ui;
            }
            public virtual FrameworkElement RandomElement(IList<Object> all, Object current, Size limit)
            {
                //System.Diagnostics.Debug.WriteLine("Limit:" + limit.Width + "," + limit.Height);
                FrameworkElement ui = null;
                if (this.OnRandomElement == null)
                {
                    List<DataTemplate> ltemp = new List<DataTemplate>();
                    foreach (DataTemplate dt in this.Templates)
                    {
                        FrameworkElement tui = dt.LoadContent() as FrameworkElement;
                        if (limit.Width > 0 && tui.Width > limit.Width)
                        {
                            continue;
                        }
                        if (limit.Height > 0 && tui.Height > limit.Height)
                        {
                            continue;
                        }
                        ltemp.Add(dt);
                    }
                    if (ltemp.Count < 1)
                    {
                        return null;
                    }
                    int tidx = rd.Next(0, ltemp.Count);
                    ui = ltemp[tidx].LoadContent() as FrameworkElement;
                }
                else
                {
                    ui = this.OnRandomElement(this, all, current, rd, limit);
                }
                if (this.OnCreateElement != null)
                {
                    this.OnCreateElement(this, ui, all, current, limit);
                }
                return ui;
            }
            private Dictionary<String, List<DataTemplate>> GroupedTemplate = new Dictionary<string, List<DataTemplate>>();
            public virtual void InitGroup(bool byWidth = true)
            {
                foreach (DataTemplate dt in this.Templates)
                {
                    FrameworkElement ui = dt.LoadContent() as FrameworkElement;
                    String key = (byWidth ? ui.Width : ui.Height) + "";
                    List<DataTemplate> gdt = null;
                    if (this.GroupedTemplate.ContainsKey(key))
                    {
                        gdt = this.GroupedTemplate[key];
                    }
                    else
                    {
                        gdt = new List<DataTemplate>();
                    }
                    gdt.Add(dt);
                    this.GroupedTemplate[key] = gdt;
                }
            }
            public virtual List<String> GroupedTemplateKeys(Double size)
            {
                if (this.OnRandomGroup != null)
                {
                    return this.OnRandomGroup(this, rd, size);
                }
                List<String> keys = new List<String>();
                foreach (String k in this.GroupedTemplate.Keys)
                {
                    Double kv = Double.Parse(k);
                    if (kv < size)
                    {
                        keys.Add(k);
                    }
                }
                return keys;
            }
            public virtual FrameworkElement RandomElement(IList<Object> all, Object current, String key)
            {
                //System.Diagnostics.Debug.WriteLine("Limit:" + limit.Width + "," + limit.Height);
                FrameworkElement ui = null;
                if (this.OnRandomElement == null)
                {
                    List<DataTemplate> ltemp = this.GroupedTemplate[key];
                    if (ltemp == null || ltemp.Count < 1)
                    {
                        return null;
                    }
                    int tidx = rd.Next(0, ltemp.Count);
                    ui = ltemp[tidx].LoadContent() as FrameworkElement;
                }
                else
                {
                    ui = this.OnRandomElement(this, all, current, rd, key);
                }
                if (this.OnCreateElement != null)
                {
                    this.OnCreateElement(this, ui, all, current, key);
                }
                return ui;
            }
            public virtual DependencyObject this[int tidx]
            {
                get
                {
                    if (tidx < 0 || tidx >= this.Templates.Count)
                    {
                        return null;
                    }
                    else
                    {
                        return this.Templates[tidx].LoadContent() as FrameworkElement;
                    }
                }
            }
            public virtual DependencyObject this[String name]
            {
                get
                {
                    if (!this.TemplSet.ContainsKey(name))
                    {
                        return null;
                    }
                    DependencyObject dt = this.TemplSet[name] as DependencyObject;
                    if (dt is DataTemplate)
                    {
                        return (dt as DataTemplate).LoadContent() as FrameworkElement;
                    }
                    else
                    {
                        return dt;
                    }
                }
            }
        }
        //
        private Canvas _ACanvas;
        public Canvas ACanvas
        {
            get
            {
                return this._ACanvas;
            }
        }
        public ScrollViewer SViewer
        {
            get
            {
                return this._SViewer;
            }
        }
        //
        private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AutoCanvas ac = d as AutoCanvas;
            if (ac == null)
            {
                return;
            }
            if (e.Property == AutoCanvas.TBuilderSourceProperty)
            {
                ac.TBuilder.Source = ac.TBuilderSource;
                return;
            }
            //
            if (ac._IsOnLoaded)
            {
                ac.LayoutElements();
            }
        }
        //
        public static readonly DependencyProperty TBuilderProperty = DependencyProperty.Register("TBuilder", typeof(DataTemplateBuilder), typeof(AutoCanvas), new PropertyMetadata(new DataTemplateBuilder(), OnPropertyChanged));
        public DataTemplateBuilder TBuilder
        {
            get
            {
                return this.GetValue(TBuilderProperty) as DataTemplateBuilder;
            }
            set
            {
                this.SetValue(TBuilderProperty, value);
            }
        }
        public static readonly DependencyProperty ItemSourcesProperty = DependencyProperty.Register("ItemSources", typeof(IList<Object>), typeof(AutoCanvas), new PropertyMetadata(null, OnPropertyChanged));
        public IList<Object> ItemSources
        {
            get
            {
                return this.GetValue(ItemSourcesProperty) as IList<Object>;
            }
            set
            {
                this.SetValue(ItemSourcesProperty, value);
                if (this._IsOnLoaded)
                {
                    this.LayoutElements();
                }
            }
        }
        public static readonly DependencyProperty ThresholdProperty = DependencyProperty.Register("Threshold", typeof(double), typeof(AutoCanvas), new PropertyMetadata(null, OnPropertyChanged));
        public double Threshold
        {
            get
            {
                return (double)this.GetValue(ThresholdProperty);
            }
            set
            {
                this.SetValue(ThresholdProperty, value);
            }
        }
        public static readonly DependencyProperty LTypeProperty = DependencyProperty.Register("LType", typeof(LayoutType), typeof(AutoCanvas), new PropertyMetadata(LayoutType.Horizontal, OnPropertyChanged));
        public LayoutType LType
        {
            get
            {
                return (LayoutType)this.GetValue(LTypeProperty);
            }
            set
            {
                this.SetValue(LTypeProperty, value);
            }
        }
        public static readonly DependencyProperty SelModeProperty = DependencyProperty.Register("SelMode", typeof(SelectionMode), typeof(AutoCanvas), new PropertyMetadata(SelectionMode.Single, OnPropertyChanged));
        public SelectionMode SelMode
        {
            get
            {
                return (SelectionMode)this.GetValue(SelModeProperty);
            }
            set
            {
                this.SetValue(SelModeProperty, value);
            }
        }
        public static readonly DependencyProperty ItemPaddingProperty = DependencyProperty.Register("ItemPadding", typeof(Thickness), typeof(AutoCanvas), new PropertyMetadata(new Thickness(0), OnPropertyChanged));
        public Thickness ItemPadding
        {
            get
            {
                return (Thickness)this.GetValue(ItemPaddingProperty);
            }
            set
            {
                this.SetValue(ItemPaddingProperty, value);
            }
        }
        public static readonly DependencyProperty ScrollableProperty = DependencyProperty.Register("Scrollable", typeof(bool), typeof(AutoCanvas), new PropertyMetadata(true, OnPropertyChanged));
        public bool Scrollable
        {
            get
            {
                return (bool)this.GetValue(ScrollableProperty);
            }
            set
            {
                this.SetValue(ScrollableProperty, value);
            }
        }
        public static readonly DependencyProperty ScrollBarVisibleProperty = DependencyProperty.Register("ScrollBarVisible", typeof(bool), typeof(AutoCanvas), new PropertyMetadata(true, OnPropertyChanged));
        public bool ScrollBarVisible
        {
            get
            {
                return (bool)this.GetValue(ScrollBarVisibleProperty);
            }
            set
            {
                this.SetValue(ScrollBarVisibleProperty, value);
            }
        }
        public static readonly DependencyProperty TargetOffsetProperty = DependencyProperty.Register("TargetOffset", typeof(double), typeof(AutoCanvas), new PropertyMetadata(null, OnPropertyChanged));
        public double TargetOffset
        {
            get
            {
                return (double)this.GetValue(TargetOffsetProperty);
            }
            set
            {
                this.SetValue(TargetOffsetProperty, value);
            }
        }
        public static readonly DependencyProperty DynamicOffsetProperty = DependencyProperty.Register("DynamicOffset", typeof(Double), typeof(AutoCanvas), new PropertyMetadata(null));
        private double _DynamicOffset
        {
            set
            {
                this.SetValue(DynamicOffsetProperty, value);
            }
        }
        public double DynamicOffset
        {
            get
            {
                return (double)this.GetValue(DynamicOffsetProperty);
            }
        }
        public static readonly DependencyProperty IsWaterfallAlignmentCenterProperty = DependencyProperty.Register("IsWaterfallAlignmentCenter", typeof(bool), typeof(AutoCanvas), new PropertyMetadata(true, OnPropertyChanged));
        public bool IsWaterfallAlignmentCenter
        {
            get
            {
                return (bool)this.GetValue(IsWaterfallAlignmentCenterProperty);
            }
            set
            {
                this.SetValue(IsWaterfallAlignmentCenterProperty, value);
            }
        }
        public static readonly DependencyProperty TBuilderSourceProperty = DependencyProperty.Register("TemplateSource", typeof(Uri), typeof(AutoCanvas), new PropertyMetadata(null, OnPropertyChanged));
        public Uri TBuilderSource
        {
            get
            {
                return (Uri)this.GetValue(TBuilderSourceProperty);
            }
            set
            {
                this.SetValue(TBuilderSourceProperty, value);
            }
        }
        public event ElementTapped OnElementTapped;
        public event SelectItems OnSelectItems;
        //
        //
        public AutoCanvas()
        {
            this.InitializeComponent();
            this._ACanvas = new Canvas();
            this.LayoutBorder.Add(new Point(0, 0));
            this.SelectedUis.CollectionChanged += SelectedUisChanged;
            this.SelectedItems.CollectionChanged += SelectedItemsChanged;
            this._IsOnLoaded = false;
            this.Loaded += OnLoaded;
            this.Unloaded += OnUnLoaded;
        }
        private bool _IsOnLoaded = false;
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            this._IsOnLoaded = true;
            if (this.TargetOffset < 10)
            {
                switch (this.LType)
                {
                    case LayoutType.Horizontal:
                    case LayoutType.Random:
                    case LayoutType.WaterfallHorizontal:
                        this.TargetOffset = this.ActualHeight;
                        break;
                    case LayoutType.Vertical:
                    case LayoutType.WaterfallVertical:
                        this.TargetOffset = this.ActualWidth;
                        break;
                }
            }
            this.LayoutElements();
        }
        private void OnUnLoaded(object sender, RoutedEventArgs e)
        {
            this._IsOnLoaded = false;
        }
        public void LayoutElements()
        {
            if (this.TargetOffset < 10)
            {
                return;
            }
            this.hoffset = 0;
            this.LayoutBorder.Clear();
            this.WfOffset.Clear();
            this.ShowAllItems();
            this.UpdateOffset();
        }
        //
        double hoffset = 0;
        List<Point> LayoutBorder = new List<Point>();
        private void UpdateOffset()
        {
            switch (this.LType)
            {
                case LayoutType.Horizontal:
                case LayoutType.Random:
                case LayoutType.WaterfallHorizontal:
                    this.ACanvas.Width = this._DynamicOffset = this.hoffset;
                    this._SViewer.VerticalScrollMode = ScrollMode.Disabled;
                    this._SViewer.HorizontalScrollMode = ScrollMode.Auto;
                    this._SViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
                    this._SViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
                    break;
                case LayoutType.Vertical:
                case LayoutType.WaterfallVertical:
                    this.ACanvas.Height = this._DynamicOffset = this.hoffset;
                    this._SViewer.VerticalScrollMode = ScrollMode.Auto;
                    this._SViewer.HorizontalScrollMode = ScrollMode.Disabled;
                    this._SViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
                    this._SViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
                    break;
            }
            if (!this.Scrollable)
            {
                this._SViewer.VerticalScrollMode = ScrollMode.Disabled;
                this._SViewer.HorizontalScrollMode = ScrollMode.Disabled;
            }
            if (!this.ScrollBarVisible)
            {
                this._SViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
                this._SViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
            }
        }
        private void ShowAllItems()
        {
            if (this.ItemSources == null || this.ItemSources.Count < 1)
            {
                return;
            }
            this.ACanvas.Children.Clear();
            if (this.LType.Equals(LayoutType.WaterfallHorizontal))
            {
                this.InitWfOffset(false);
            }
            else if (this.LType.Equals(LayoutType.WaterfallVertical))
            {
                this.InitWfOffset(true);
            }
            for (int i = 0; i < this.ItemSources.Count; )
            {
                FrameworkElement ui = null;
                switch (this.LType)
                {
                    case LayoutType.Horizontal:
                        ui = this.TBuilder.RandomElement(this.ItemSources, this.ItemSources[i]);
                        if (ui == null)
                        {
                            throw new InvalidDataException("can't create Template");
                        }
                        this.ShowHorizontalElement(ui);
                        break;
                    case LayoutType.Vertical:
                        ui = this.TBuilder.RandomElement(this.ItemSources, this.ItemSources[i]);
                        if (ui == null)
                        {
                            throw new InvalidDataException("can't create Template");
                        }
                        this.ShowVerticalElement(ui);
                        break;
                    case LayoutType.Random:
                        ui = this.ShowRandomElement(this.ItemSources[i]);
                        break;
                    case LayoutType.WaterfallHorizontal:
                    case LayoutType.WaterfallVertical:
                        ui = this.ShowWaterfall(this.ItemSources[i]);
                        break;
                }
                //init DataContext.
                Object bobj;
                if (ui.GetType().Equals(typeof(GridSubs)))
                {
                    GridSubs gsub = (GridSubs)ui;
                    int scount = gsub.SubCount;
                    GridSubs.BindObj _bobj = new GridSubs.BindObj();
                    int tcount = (this.ItemSources.Count - i) > scount ? (i + scount) : this.ItemSources.Count;
                    for (; i < tcount; i++)
                    {
                        _bobj.ItemObjs.Add(this.ItemSources[i]);
                    }
                    bobj = _bobj;
                }
                else
                {
                    bobj = this.ItemSources[i];
                    i++;
                }
                ui.DataContext = bobj;
                ui.Tapped += _ElementTapped;
                if (this.SelectedItems.Contains(bobj))
                {
                    this.OnSelectedItem(ui, bobj);
                }
                else
                {
                    this.SetStyle(ui, false);
                }
                this.ACanvas.Children.Add(ui);
            }
            this._SViewer.Content = this._ACanvas;
        }
        private void SetStyle(FrameworkElement ui, bool selected)
        {
            String key;
            if (selected)
            {
                key = ui.GetType().Name + "SelectedStyle";
            }
            else
            {
                key = ui.GetType().Name + "NormalStyle";
            }
            Style s = this.TBuilder[key] as Style;
            if (s != null && ui.GetType().Equals(s.TargetType))
            {
                ui.Style = s;
                //ui.UpdateLayout();
            }
        }
        private ObservableCollection<Object> _SelectedItems = new ObservableCollection<Object>();
        public ObservableCollection<Object> SelectedItems
        {
            get
            {
                return this._SelectedItems;
            }
        }
        private ObservableCollection<FrameworkElement> SelectedUis = new ObservableCollection<FrameworkElement>();
        public void SelectedItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (this.OnSelectItems != null)
            {
                this.OnSelectItems(this, e);
            }
        }
        public void SelectedUisChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (FrameworkElement ui in e.OldItems)
                {
                    this.SetStyle(ui, false);
                }
            }
            if (e.NewItems != null)
            {
                foreach (FrameworkElement ui in e.NewItems)
                {
                    this.SetStyle(ui, true);
                }
            }
        }
        private void OnSelectedItem(FrameworkElement ui, Object obj)
        {
            if (ui == null || obj == null)
            {
                return;
            }
            if (this.SelectedUis.Contains(ui))
            {
                if (SelectionMode.Single.Equals(this.SelMode))
                {
                    return;
                }
                else
                {
                    this.SelectedUis.Remove(ui);
                    this.SelectedItems.Remove(obj);
                    return;
                }
            }
            switch (this.SelMode)
            {
                case SelectionMode.Single:
                    if (this.SelectedItems.Count > 0)
                    {
                        this.SelectedItems.RemoveAt(0);
                    }
                    this.SelectedItems.Add(obj);
                    if (this.SelectedUis.Count > 0)
                    {
                        this.SelectedUis.RemoveAt(0);
                    }
                    this.SelectedUis.Add(ui);
                    break;
                case SelectionMode.Extended:
                case SelectionMode.Multiple:
                    this.SelectedItems.Add(obj);
                    this.SelectedUis.Add(ui);
                    break;
            }
        }
        private void _ElementTapped(Object sender, TappedRoutedEventArgs e)
        {
            FrameworkElement ui = sender as FrameworkElement;
            //Point current = e.GetPosition(null);
            //IEnumerable<UIElement> stree = VisualTreeHelper.FindElementsInHostCoordinates(current, ui, true);
            Object dcontext = ui.DataContext;
            //foreach (FrameworkElement sui in stree)
            //{
            //    if (sui != sender)
            //    {
            //        dcontext = sui.DataContext;
            //        break;
            //    }
            //}
            if (dcontext == null)
            {
                return;
            }
            this.OnSelectedItem(ui, dcontext);
            if (this.OnElementTapped != null)
            {
                this.OnElementTapped(dcontext, sender, e);
            }
        }
        void ShowHorizontalElement(FrameworkElement ui)
        {
            this.hoffset += this.ItemPadding.Left;
            Canvas.SetLeft(ui, hoffset);
            Canvas.SetTop(ui, this.ItemPadding.Top);
            ui.Height = this.TargetOffset - this.ItemPadding.Top - this.ItemPadding.Bottom;
            UIElement o = this.Parent as UIElement;
            hoffset += ui.Width + this.ItemPadding.Right;
        }
        void ShowVerticalElement(FrameworkElement ui)
        {
            this.hoffset += this.ItemPadding.Top;
            Canvas.SetLeft(ui, this.ItemPadding.Left);
            Canvas.SetTop(ui, hoffset);
            ui.Width = this.TargetOffset - this.ItemPadding.Left - this.ItemPadding.Right;
            hoffset += ui.Height + this.ItemPadding.Bottom;
        }
        FrameworkElement ShowRandomElement(Object current)
        {
            FrameworkElement ui = null;
            if (this.LayoutBorder.Count < 1)
            {
                throw new InvalidDataException("not layout border found");
            }
            Size limit = new Size(0, 0);
            Point first = this.LayoutBorder[0];
            this.LayoutBorder.RemoveAt(0);
            limit.Width = 0;
            double lheight = 99999;
            for (int i = 0; i < this.LayoutBorder.Count; i++)
            {
                if (this.LayoutBorder[i].Y >= first.Y && this.LayoutBorder[i].X >= first.X)
                {
                    double lhtmp = this.LayoutBorder[i].Y - first.Y - this.ItemPadding.Bottom;
                    if (lhtmp < lheight)
                    {
                        lheight = lhtmp;
                    }
                }
            }

            if (lheight == 99999)
            {
                lheight = this.ActualHeight - first.Y - this.ItemPadding.Bottom;
            }
            if (lheight < 1)
            {
                return this.ShowRandomElement(current);
            }
            limit.Height = lheight;
            ui = this.TBuilder.RandomElement(this.ItemSources, current, limit);
            if (ui == null)
            {
                return this.ShowRandomElement(current);
            }
            Canvas.SetLeft(ui, first.X + this.ItemPadding.Left);
            Canvas.SetTop(ui, first.Y + this.ItemPadding.Top);
            double ny = first.Y + ui.Height + ui.Margin.Bottom + ui.Margin.Top + this.ItemPadding.Bottom;
            double nx = first.X + this.ItemPadding.Left + ui.Width + ui.Margin.Left + ui.Margin.Right;
            this.LayoutBorder.Add(new Point(first.X, ny));
            this.LayoutBorder.Add(new Point(nx, first.Y));
            if (nx > this.hoffset)
            {
                this.hoffset = nx;
            }
            this.SortLayoutBorder();
            return ui;
        }
        void SortLayoutBorder()
        {
            this.LayoutBorder.Sort(delegate(Point a, Point b)
            {
                if (a.X < b.X)
                {
                    return -1;
                }
                if (a.X > b.X)
                {
                    return 1;
                }
                if (a.Y < b.Y)
                {
                    return -1;
                }
                if (a.Y > b.Y)
                {
                    return 1;
                }
                return 0;
            });

        }
        private List<KeyValuePair<String, Point>> WfOffset = new List<KeyValuePair<String, Point>>();
        void InitWfOffset(bool byWidth)
        {
            List<KeyValuePair<String, Point>> TmpWfOffset = new List<KeyValuePair<String, Point>>();
            this.TBuilder.InitGroup(byWidth);
            Random rd = new Random();
            List<String> gtkey = new List<String>();
            double offset = 0;
            double maxoffset = this.TargetOffset;
            while (offset < maxoffset)
            {
                gtkey.Clear();
                if (this.LType == LayoutType.WaterfallHorizontal)
                {
                    gtkey.AddRange(this.TBuilder.GroupedTemplateKeys(maxoffset - offset - this.ItemPadding.Top - this.ItemPadding.Bottom));
                }
                else
                {
                    gtkey.AddRange(this.TBuilder.GroupedTemplateKeys(maxoffset - offset - this.ItemPadding.Left - this.ItemPadding.Right));
                }
                if (gtkey.Count < 1)
                {
                    break;
                }
                while (gtkey.Count > 0)
                {
                    int idx = rd.Next(0, gtkey.Count);
                    Double iw = Double.Parse(gtkey[idx]);
                    Point pt = new Point();
                    double noffset = 0;
                    if (this.LType == LayoutType.WaterfallHorizontal)
                    {
                        offset += this.ItemPadding.Top;
                        pt.X = rd.NextDouble() * this.Threshold;
                        pt.Y = offset;
                        noffset = offset + iw + this.ItemPadding.Bottom;
                    }
                    else
                    {
                        offset += this.ItemPadding.Left;
                        pt.X = offset;
                        pt.Y = rd.NextDouble() * this.Threshold;
                        noffset = offset + iw + this.ItemPadding.Right;
                    }
                    if (noffset > maxoffset)
                    {
                        break;
                    }
                    offset = noffset;
                    TmpWfOffset.Add(new KeyValuePair<string, Point>(gtkey[idx], pt));
                    gtkey.RemoveAt(idx);
                }
            }


            double aoffset = (maxoffset - offset) / 2;
            foreach (KeyValuePair<string, Point> kv in TmpWfOffset)
            {
                Point pt = kv.Value;
                if (this.IsWaterfallAlignmentCenter)
                {
                    if (this.LType == LayoutType.WaterfallHorizontal)
                    {
                        pt.Y += aoffset;
                    }
                    else
                    {
                        pt.X += aoffset;
                    }
                }
                this.WfOffset.Add(new KeyValuePair<string, Point>(kv.Key, pt));
            }
        }
        FrameworkElement ShowWaterfall(Object current)
        {
            //System.Diagnostics.Debug.WriteLine("WfOffset:" + this.WfOffset.Count);
            if (this.WfOffset.Count < 1)
            {
                throw new InvalidDataException("the DataTemplate is empty");
            }
            KeyValuePair<String, Point> minKv;
            Double minOffset = 99999999.0;
            Double maxOffset = 0;
            foreach (KeyValuePair<String, Point> k in this.WfOffset)
            {
                Double kv;
                if (this.LType == LayoutType.WaterfallHorizontal)
                {
                    kv = k.Value.X;
                }
                else
                {
                    kv = k.Value.Y;
                }
                if (minOffset > kv)
                {
                    minOffset = kv;
                    minKv = k;
                }
                if (kv > maxOffset)
                {
                    maxOffset = kv;
                }
            }
            this.WfOffset.Remove(minKv);
            FrameworkElement ui = this.TBuilder.RandomElement(this.ItemSources, current, minKv.Key);
            Point pt = minKv.Value;
            if (this.LType == LayoutType.WaterfallHorizontal)
            {
                if (ui.Height != Double.Parse(minKv.Key))
                {
                    throw new InvalidDataException("the RandomElement return FrameworkElement height not corrent");
                }
                pt.X += this.ItemPadding.Left;
            }
            else
            {
                if (ui.Width != Double.Parse(minKv.Key))
                {
                    throw new InvalidDataException("the RandomElement return FrameworkElement height not corrent");
                }
                pt.Y += this.ItemPadding.Top;
            }
            Canvas.SetLeft(ui, pt.X);
            Canvas.SetTop(ui, pt.Y);
            if (this.LType == LayoutType.WaterfallHorizontal)
            {
                pt.X += ui.Width + this.ItemPadding.Right;
                if (pt.X > maxOffset)
                {
                    maxOffset = pt.X;
                }
            }
            else
            {
                pt.Y += ui.Height + this.ItemPadding.Bottom;
                if (pt.Y > maxOffset)
                {
                    maxOffset = pt.Y;
                }
            }
            this.hoffset = maxOffset;
            minKv = new KeyValuePair<string, Point>(minKv.Key, pt);
            this.WfOffset.Add(minKv);
            return ui;
        }
    }
}
